package com.meshnetics.gb.util.formatters;

import java.nio.ByteBuffer;

/**
 * Universal byte buffer formatter. In accordance with passed parameters in constructor formats
 * byte buffers to strings. For example: length: 3 [10 (0x0A), 127 (0x7F), 255 (0xFF)]
 * <p><b> NOTE: </b> source buffer duplicated before formatting to avoid position marker changing </p>
 *
 * @author Yury Aserchev (yaserchev@meshnetics.com)
 *         Date: 27.08.2007 13:12:14
 */
public final class UniversalByteBufferFormat extends AbstractByteBufferFormat{
    public static final ByteFormatter DEFAULT_FORMATTER = new ByteFormatterHexAndUByte();
    private final boolean addBrackets;
    private final String separator;
    private final String prefix;
    private final ByteFormatter byteFormatter;
    private final boolean addLength;

    /**
     * Creates formatter in accordance with passed parameters. Sequence of used brackets, prefixes and
     * etc.: The first is length (if true), then "[" bracket follows (if true), then prefix follows
     * (if not null), then formatted bytes follow separated by separator (if not null) and the
     * last "]" bracket follows (if true). <p> For example: </p> <p>length: 2 [0x02:FF]</p>
     * <p>length: 3 [10, 18, 119]</p><p>90, 111, 0, -1</p>Each byte is formatted in according to
     * passed byteFormatter.
     * @param addBrackets is it need to add brackets "[" and "]" before and after bytes of buffer.
     * For example: 0, 10, 12, 100 or [0, 10, 12, 100].
     * @param separator what separator is used for separating bytes of buffer. None if null;
     * For example: Separator is ":" 0:10:12:100; Separator is ", " 0, 10, 12, 100; Separator is null 01012100.
     * @param prefix common prefix for all bytes. For example: Prefix is "0x" [0x10:11:FF];
     * Prefix is null [10:11:FF].
     * @param byteFormatter not null byte formatter.
     * @param addLength is it need to add buffer length before formatted bytes. Length (if true) being
     * added as decimal with ":" symbol. For example: length 3: [1, 2, 119]; length 0: [].
     * @throws NullPointerException if byte formatter is null.
     */
    public UniversalByteBufferFormat(final boolean addBrackets, final String separator, final String prefix,
                                     final ByteFormatter byteFormatter, final boolean addLength){
        if (byteFormatter == null) {
            throw new IllegalArgumentException("byteFormatter is null");
        }
        this.addBrackets = addBrackets;
        this.prefix = prefix;
        this.separator = separator;
        this.byteFormatter = byteFormatter;
        this.addLength = addLength;
    }

    /**
     * Default constructor. Equals constructor with parameters: addBrackets: true;
     * separator: ", "; prefix: null; byteFormatter: DEFAULT_FORMATTER; addLength: true;
     * <br/>Example of formatted String: <code>length: 3 [0 (0x00), 255 (0xFF), 127 (0x7F)]</code>
     * <br/>See {@link UniversalByteBufferFormat#UniversalByteBufferFormat(boolean, String,
            String, ByteFormatter, boolean)} for details
     */
    public UniversalByteBufferFormat(){
        this(true, ", ", null, DEFAULT_FORMATTER, true);
    }

    public String format(final ByteBuffer byteBuffer) {
        final ByteBuffer copy = duplicateBuffer(byteBuffer);
        final StringBuffer buff = new StringBuffer("");
        if (addLength){
            buff.append("length ");
            buff.append(byteBuffer.capacity());
            buff.append(" ");
        }
        if (addBrackets){
            buff.append("[");
        }
        if (prefix != null){
            buff.append(prefix);
        }
        while (copy.position() < copy.capacity()){
            final byte b = copy.get();
            buff.append(byteFormatter.format(b));
            if (separator != null && copy.position() < copy.capacity()){
                buff.append(separator);
            }
        }
        if (addBrackets){
            buff.append("]");
        }
        return buff.toString();
    }

    boolean isAddBrackets() {
        return addBrackets;
    }

    String getSeparator() {
        return separator;
    }

    String getPrefix() {
        return prefix;
    }

    ByteFormatter getByteFormatter() {
        return byteFormatter;
    }

    boolean isAddLength() {
        return addLength;
    }
}
